﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Newtonsoft.Json;

namespace OF.JsonRpc.Client.RpcInvokers
{
    internal abstract class RpcInvoker
    {
        protected static Encoding UTF8Encoding = new UTF8Encoding(false);

        protected static object idLock = new object();
        protected static int id = 0;
        public RpcOption Option { get; set; }
        internal string ServiceAddress { get; set; }

        internal JsonResponse<T> Invoke<T>(string method, params object[] args)
        {
            var req = new JsonRequest
            {
                Method = method,
                Params = args
            };

            int myId;

            if (req.Id == null)
            {
                lock (idLock)
                {
                    myId = ++id;
                }

                req.Id = myId.ToString();
            }

            AttachRpcContext(req);
            AttachAuthOfClient(req);
            return Invoke<T>(req);
        }

        protected void AttachAuthOfClient(JsonRequest req)
        {
            if (req.Tags.ContainsKey("auth_client_id") && !string.IsNullOrWhiteSpace(req.Tags["auth_token"].ToString()))
                return;

            // 优先使用req.Tags已经传入的auth_client_id，没有则使用config的
            if (!req.Tags.ContainsKey("auth_client_id") ||
                string.IsNullOrWhiteSpace(req.Tags["auth_client_id"].ToString()))
            {
                var clientIdFromConfig = GetClientIdFromConfig(req.Method);
                if (clientIdFromConfig == null)
                    return;
                else
                    req.Tags["auth_client_id"] = clientIdFromConfig;
            }

            var client_id = req.Tags["auth_client_id"].ToString();
            var privateKeyFile = AppDomain.CurrentDomain.BaseDirectory + "\\keys\\private_key_" + client_id + ".pem";
            if (!File.Exists(privateKeyFile))
            {
                System.Diagnostics.Debug.WriteLine("Rpc私钥证书文件不存在: " + privateKeyFile);
                return;
            }
            var privateKeyContent = File.ReadAllText(privateKeyFile);
            var plainText = JsonConvert.SerializeObject(req);
            try
            {
                var auth_token = RpcRSA.CreateSignature(plainText, privateKeyContent);
                req.Tags["auth_token"] = auth_token;
            }
            catch (Exception ex)
            {
                throw new Exception("构建服务请求签名失败", ex);
            }
        }

        #region get service url from config

        private static string GetClientIdDomainFromConfig(string method)
        {
            var domain = ExtractDomainName(method);
            if (string.IsNullOrEmpty(domain))
                return null;
            return System.Configuration.ConfigurationManager.AppSettings["JsonRpcServiceClientId." + domain];
        }

        private static string ExtractDomainName(string method)
        {
            var elements = method.Split('.');
            return elements.Length >= 2 ? elements[0] : null;
        }

        // 调用时用这个就可以了
        private static string GetClientIdFromConfig(string method)
        {
            var clientIdByDomain = GetClientIdDomainFromConfig(method);
            string clientId = System.Configuration.ConfigurationManager.AppSettings["JsonRpcServiceClientId"];
            if (!string.IsNullOrWhiteSpace(clientIdByDomain))
                clientId = clientIdByDomain;
            return clientId;
        }

        #endregion

        internal JsonResponse<T> InvokeWithDeclaredParams<T>(string method, object args)
        {
            var req = new JsonRequest
            {
                Method = method,
                Params = args,
                Id = 1
            };
            AttachRpcContext(req);
            AttachAuthOfClient(req);
            return Invoke<T>(req);
        }

        #region context process

        protected static void MergeContextValues(IDictionary<string, object> globalValues, RpcOption option)
        {
            if (option == null || option.ContextValues == null)
                return;

            foreach (var key in option.ContextValues.Keys)
                globalValues[key] = option.ContextValues[key];
        }

        // 处理上下文，合并不同时机的上下文到JsonRequest.Tags中
        protected void AttachRpcContext(JsonRequest jsonRpc)
        {
            if (jsonRpc.Tags == null)
                jsonRpc.Tags = new Dictionary<string, object>();

            // 引用全局事件中的上下文注入函数，从而将其加入到JsonRequest.Tags
            if (Rpc.GlobalContextSet != null)
                Rpc.GlobalContextSet(jsonRpc.Tags);

            // 合并当前Invoker中到JsonRequest.Tags
            MergeContextValues(jsonRpc.Tags, Option);

            // 系统预定义的上下文
            if (Option != null && Option.ForceWriteDB)
                jsonRpc.Tags["x-rpc-forcewritedb"] = "true";
        }

        #endregion


        #region abstract methods

        protected abstract JsonResponse<T> Invoke<T>(JsonRequest jsonRpc);

        internal abstract List<JsonResponse> BatchInvoke(List<JsonRequest> jsonRpcList);

        #endregion
    }
}